package com.compunit.composite.core;

import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.compunit.interfaces.IComponent;

import com.compunit.composite.core.interfaces.IAssembler;
import com.compunit.composite.core.interfaces.cx.ICxBinding;
import com.compunit.composite.core.interfaces.cx.ICxImporter;
import com.compunit.composite.core.interfaces.cx.ICxUnit;

/**
 * <code>Assembler</code> defines the implementation
 * of the component assembling utility unit.
 *
 * @author Yi Wang (Neakor)
 * @version Creation date: 11-13-2009 18:28 EST
 * @version Modified date: 11-21-2009 19:09 EST
 */
public class Assembler implements IAssembler {
	/**
	 * The <code>Map</code> of <code>String</code> fully
	 * qualified component class name to <code>IComponent</code>
	 * instances loaded.
	 * <p>
	 * This map is used for binding only. It does not
	 * preserve the activation order.
	 */
	private final Map<String, IComponent> cmap;
	/**
	 * The <code>List</code> of <code>CompUnit</code>
	 * in their activation order.
	 */
	private final List<CompUnit> clist;
	
	/**
	 * Constructor of <code>Assembler</code>.
	 */
	public Assembler() {
		this.cmap = new HashMap<String, IComponent>();
		this.clist = new ArrayList<CompUnit>();
	}

	@Override
	public Iterable<CompUnit> assemble(final ICxImporter importer, final URL jar) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
		this.cleanup();
		final URLClassLoader loader = new URLClassLoader(new URL[] {jar});
		final Iterable<ICxUnit> units = importer.getActivation();
		for(final ICxUnit unit : units) {
			final Class<?> classobject = loader.loadClass(unit.getClassname());
			final IComponent instance = (IComponent)classobject.newInstance();
			this.cmap.put(unit.getClassname(), instance);
			this.clist.add(new CompUnit(instance, unit.getResourcename()));
		}
		final Iterable<ICxBinding> bindings = importer.getBindings();
		for(final ICxBinding b : bindings) {
			final String provider = b.getProvider();
			final String consumer = b.getConsumer();
			final IComponent p = this.cmap.get(provider);
			if(p == null) throw new ClassNotFoundException(provider);
			final IComponent c = this.cmap.get(consumer);
			if(c == null) throw new ClassNotFoundException(consumer);
			try {
				c.connect(p, provider);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return this.getComponents();
	}

	@Override
	public Iterable<CompUnit> getComponents() {
		return this.clist;
	}

	@Override
	public void cleanup() {
		this.cmap.clear();
	}
}
